package com.example.api.config;

import com.example.api.annotation.ApiVersion;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * @Description 自定义请求映射处理
 * @Author jie.zhao
 * @Date 2021/9/10 15:38
 */
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    private final static Pattern VERSION_NUMBER_PATTERN = Pattern.compile("^\\d+(\\.\\d+){0,2}$");

    private ApiVersionConfigProperties apiVersionConfigProperties;

    public ApiVersionRequestMappingHandlerMapping(ApiVersionConfigProperties apiVersionConfigProperties) {
        this.apiVersionConfigProperties = apiVersionConfigProperties;
    }

    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        return createRequestCondition(handlerType);
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        return createRequestCondition(method);
    }

    private RequestCondition<ApiVersionRequestCondition> createRequestCondition(AnnotatedElement target) {
        if (apiVersionConfigProperties.getType() == ApiTypeEnum.URI) {
            return null;
        }
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(target, ApiVersion.class);
        if (apiVersion == null) {
            return null;
        }
        String version = apiVersion.value().trim();
        checkVersionNumber(version, target);
        return new ApiVersionRequestCondition(version, apiVersionConfigProperties);
    }

    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo info = this.createRequestMappingInfo(method);
        if (info != null) {
            RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
            if (apiVersionConfigProperties.getType() == ApiTypeEnum.URI) {
                ApiVersion apiVersion = AnnotationUtils.getAnnotation(method, ApiVersion.class);
                if (apiVersion == null) {
                    apiVersion = AnnotationUtils.getAnnotation(handlerType, ApiVersion.class);
                }
                if (apiVersion != null) {
                    String version = apiVersion.value().trim();
                    checkVersionNumber(version, method);
                    String prefix = "/v" + version;
                    info = RequestMappingInfo.paths(new String[]{prefix}).build().combine(info);
                }
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class) element) : this.getCustomMethodCondition((Method) element);
        return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
    }

    /**
     * 判断是否满足版本号规则
     */
    private void checkVersionNumber(String version, Object targetMethodOrType) {
        if (version.length() == 0) {
            throw new IllegalArgumentException(String.format("版本号不能为空: @ApiVersion(\"%s\") at %s", version, targetMethodOrType));
        }
        if (!VERSION_NUMBER_PATTERN.matcher(version).find()) {
            throw new IllegalArgumentException(String.format("版本号不满足规则格式：0.0.0: @ApiVersion(\"%s\") at %s", version, targetMethodOrType));
        }
    }
}